﻿using System;
using Windows.Foundation;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media.Animation;

namespace Ebt.Mvvm.Behaviors
{
    public static class PopupBehavior
    {
        private const int PopInAnimationOffset = 80;

        #region VerticalOffset attached property

        public static readonly DependencyProperty VerticalOffsetProperty =
            DependencyProperty.RegisterAttached("VerticalOffset", typeof(double), typeof(PopupBehavior), null);

        public static void SetVerticalOffset(DependencyObject d, double value)
        {
            d.SetValue(VerticalOffsetProperty, value);
        }

        public static double GetVerticalOffset(DependencyObject d)
        {
            return (double)d.GetValue(VerticalOffsetProperty);
        }

        #endregion

        #region HorizontalOffset attached property

        public static readonly DependencyProperty HorizontalOffsetProperty =
            DependencyProperty.RegisterAttached("HorizontalOffset", typeof(double), typeof(PopupBehavior), null);

        public static void SetHorizontalOffset(DependencyObject d, double value)
        {
            d.SetValue(HorizontalOffsetProperty, value);
        }

        public static double GetHorizontalOffset(DependencyObject d)
        {
            return (double)d.GetValue(HorizontalOffsetProperty);
        }

        #endregion

        #region Placement attached property

        public static readonly DependencyProperty PlacementProperty =
            DependencyProperty.RegisterAttached("Placement", typeof(Placement), typeof(PopupBehavior), new PropertyMetadata(Placement.Default));

        public static void SetPlacement(DependencyObject d, Placement value)
        {
            d.SetValue(PlacementProperty, value);
        }

        public static Placement GetPlacement(DependencyObject d)
        {
            return (Placement)d.GetValue(PlacementProperty);
        }

        #endregion

        #region PlacementTarget attached property

        public static readonly DependencyProperty PlacementTargetProperty =
            DependencyProperty.RegisterAttached("PlacementTarget", typeof(UIElement), typeof(PopupBehavior), new PropertyMetadata(null, OnPlacementTargetPropertyChanged));

        public static void SetPlacementTarget(DependencyObject d, UIElement value)
        {
            d.SetValue(PlacementTargetProperty, value);
        }

        public static UIElement GetPlacementTarget(DependencyObject d)
        {
            return (UIElement)d.GetValue(PlacementTargetProperty);
        }

        private static void OnPlacementTargetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion

        #region Content attached property

        public static readonly DependencyProperty ContentProperty =
            DependencyProperty.RegisterAttached("Content", typeof(DataTemplate), typeof(PopupBehavior),
            new PropertyMetadata(null, OnContentChanged));

        public static void SetContent(DependencyObject d, DataTemplate value)
        {
            d.SetValue(ContentProperty, value);
        }

        public static DataTemplate GetContent(DependencyObject d)
        {
            return (DataTemplate)d.GetValue(ContentProperty);
        }

        #endregion

        #region LightDismiss attached property

        public static readonly DependencyProperty LightDismissProperty =
            DependencyProperty.RegisterAttached("LightDismiss", typeof(LightDismiss), typeof(PopupBehavior), new PropertyMetadata(LightDismiss.Tap, null));

        public static void SetLightDismiss(DependencyObject d, LightDismiss value)
        {
            d.SetValue(LightDismissProperty, value);
        }

        public static LightDismiss GetLightDismiss(DependencyObject d)
        {
            return (LightDismiss)d.GetValue(LightDismissProperty);
        }

        #endregion


        private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d == null)
                return;

            var element = (UIElement)d;

            element.Tapped -= OnTapped;
            element.RightTapped -= OnRightTapped;

            element.Tapped += OnTapped;
            element.RightTapped += OnRightTapped;
        }

        private static void OnRightTapped(object sender, RightTappedRoutedEventArgs e)
        {
            OpenPopup((FrameworkElement)sender);
            e.Handled = true;
        }

        private static void OnTapped(object sender, TappedRoutedEventArgs e)
        {
            OpenPopup((FrameworkElement)sender);
            e.Handled = true;
        }

        private static void OpenPopup(FrameworkElement sourceElement)
        {
            DataTemplate contentTemplate = GetContent(sourceElement);

            if (contentTemplate == null)
                return;

            var content = new ContentControl { ContentTemplate = contentTemplate, DataContext = sourceElement.DataContext };

            var popup = new Popup
            {
                Child = content,
                IsLightDismissEnabled = (GetLightDismiss(sourceElement) & LightDismiss.TapOutside) != 0
            };

            content.AddHandler(UIElement.TappedEvent,
                new TappedEventHandler(delegate
                {
                    if ((GetLightDismiss(sourceElement) & LightDismiss.TapInside) != 0)
                        popup.IsOpen = false;
                })
                , false);

            popup.IsOpen = true;

            ApplyPlacement(sourceElement, popup, content);
        }

        private static void ApplyPlacement(FrameworkElement sourceElement, Popup popup, FrameworkElement content)
        {
            var target = (FrameworkElement)GetPlacementTarget(sourceElement) ?? sourceElement;

            var anumation = new PopInThemeAnimation { FromHorizontalOffset = 0, FromVerticalOffset = 0 };
            var storyboard = new Storyboard();
            storyboard.Children.Add(anumation);
            Storyboard.SetTarget(storyboard.Children[0], content);

            content.UpdateLayout();

            var placement = GetPlacement(sourceElement);

            double x = 0, y = 0;

            switch (placement)
            {
                case Placement.Above:
                    y = -content.ActualHeight;
                    anumation.FromVerticalOffset = PopInAnimationOffset;
                    break;
                case Placement.Below:
                    y = target.ActualHeight;
                    anumation.FromVerticalOffset = PopInAnimationOffset;
                    break;
                case Placement.Left:
                    x = -content.ActualWidth;
                    anumation.FromHorizontalOffset = PopInAnimationOffset;
                    break;
                case Placement.Right:
                    x = target.ActualWidth;
                    anumation.FromHorizontalOffset = PopInAnimationOffset;
                    break;
                default:
                    anumation.FromHorizontalOffset = PopInAnimationOffset;
                    break;
            }
            x += GetHorizontalOffset(sourceElement);
            y += GetVerticalOffset(sourceElement);

            var transformToRoot = target.TransformToVisual(null);
            var p = transformToRoot.TransformPoint(new Point(x, y));
            x = p.X;
            y = p.Y;

            var rootElement = (FrameworkElement)Window.Current.Content;

            x = Math.Max(Math.Min(rootElement.ActualWidth - content.ActualWidth, x), 0);
            y = Math.Max(Math.Min(rootElement.ActualHeight - content.ActualHeight, y), 0);

            popup.HorizontalOffset = x;
            popup.VerticalOffset = y;

            storyboard.Begin();
        }
    }

    [Flags]
    public enum LightDismiss
    {
        Disabled = 0,
        TapOutside = 1,
        TapInside = 2,
        Tap = 3,
    }
}
